package cn.yzj.utils;

import cn.yzj.entity.JwtArgsConfig;
import com.alibaba.fastjson.JSONArray;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * jwt 工具类
 *
 * <p>TODO: 需要将 {@link JwtUtils} 进行代码切割
 *
 * @author gzkemays
 * @since 2022/1/14 22:58
 */
public class JwtUtils {
  private String subject;
  private String secret;
  private String header;
  private String prefix;
  private String roleClams;
  private long expired;
  private String token;
  private boolean prefixHeader;

  public static JwtUtilsBuilder builder() {
    return new JwtUtilsBuilder();
  }

  public static JwtUtilsBuilder parseBuilder(String token) {
    return new JwtUtilsBuilder(token);
  }

  public static JwtUtilsBuilder parseBuilder(String header, HttpServletRequest request) {
    return new JwtUtilsBuilder(header, request);
  }

  public static final class JwtUtilsBuilder {
    private String subject = "yzj-subject";
    private String secret = "yzj-secret";
    private String header = "token";
    private String prefix = "Bearer";
    private String roleClams = "roles";
    private long expired = 1000 * 24 * 60 * 60 * 7;
    private boolean prefixHeader;
    private String token = null;

    public JwtUtilsBuilder(String token) {
      this.token = token;
    }

    public JwtUtilsBuilder(String header, HttpServletRequest request) {
      String token = request.getHeader(header);
      this.header = header;
      if (StringUtils.isNotBlank(token)) {
        this.token = token;
      } else {
        throw new NullPointerException("request 中 token 为空，请确认 header 是否为：" + header);
      }
    }

    private JwtUtilsBuilder() {}

    public JwtUtilsBuilder subject(String subject) {
      this.subject = subject;
      return this;
    }

    public JwtUtilsBuilder prefixHeader() {
      this.prefixHeader = true;
      return this;
    }

    public JwtUtilsBuilder secret(String secret) {
      this.secret = secret;
      return this;
    }

    public JwtUtilsBuilder header(String header) {
      this.header = header;
      return this;
    }

    public JwtUtilsBuilder prefix(String prefix) {
      this.prefix = prefix;
      return this;
    }

    public JwtUtilsBuilder roleClams(String roleClams) {
      this.roleClams = roleClams;
      return this;
    }

    public JwtUtilsBuilder expired(long expired) {
      this.expired = expired;
      return this;
    }

    public JwtUtils build() {
      JwtUtils jwtUtils = new JwtUtils();
      jwtUtils.expired = this.expired;
      jwtUtils.roleClams = this.roleClams;
      jwtUtils.secret = this.secret;
      jwtUtils.header = this.header;
      jwtUtils.prefix = this.prefix;
      jwtUtils.subject = this.subject;
      jwtUtils.token = this.token;
      jwtUtils.prefixHeader = this.prefixHeader;
      return jwtUtils;
    }
  }

  public String generate(JwtArgsConfig config) {
    JwtBuilder jwtBuilder =
        Jwts.builder()
            .setSubject(subject)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + expired))
            .signWith(SignatureAlgorithm.HS256, secret);
    if (config.getAuthorities() != null) {
      Map<String, Object> map =
          new HashMap<String, Object>(1) {
            {
              put(roleClams, config.getAuthorities());
            }
          };
      jwtBuilder.setClaims(map);
    }
    Map<String, Object> data = config.getData();
    if (data != null && !data.isEmpty()) {
      jwtBuilder.setClaims(data);
    }
    String compact = jwtBuilder.compact();
    return prefixHeader ? prefix + " " + compact : compact;
  }

  private Claims getClaims() {
    if (prefixHeader) {
      String truth = token.replace(prefix, "").trim();
      System.out.println(truth);
      return Jwts.parser().setSigningKey(secret).parseClaimsJws(truth).getBody();
    }
    return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
  }

  public Object getClaim(String key) {
    return getClaims().get(key);
  }

  public <T> T getClaim(String key, Class<T> clazz) {
    return getClaims().get(key, clazz);
  }

  public Object getClaim(String token, String key) {
    this.token = token;
    return getClaims().get(key);
  }

  public <T> T getClaim(String token, String key, Class<T> clazz) {
    this.token = token;
    return getClaims().get(key, clazz);
  }
  /**
   * 检测 jwt
   *
   * @param token token信息
   * @return 声明信息
   */
  public Claims checkJwt(String token) {
    this.token = token;
    try {
      return getClaims();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  public Claims checkJwt() {
    try {
      return getClaims();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 是否过期
   *
   * @param token token信息
   * @return 判断 expire 是否过期
   */
  public boolean isExpiration(String token) {
    this.token = token;
    return getClaims().getExpiration().before(new Date());
  }

  public boolean isExpiration() {
    return getClaims().getExpiration().before(new Date());
  }

  /**
   * 获取用户角色
   *
   * @param token token 信息
   * @return 用户角色定位
   */
  @SuppressWarnings("all")
  public List<SimpleGrantedAuthority> getRoles(String token) {
    this.token = token;
    Claims claims = getClaims();
    List roles = (List) claims.get(roleClams);
    String json = JSONArray.toJSONString(roles);
    List<SimpleGrantedAuthority> grantedAuthorityList =
        JSONArray.parseArray(json, SimpleGrantedAuthority.class);
    return grantedAuthorityList;
  }

  @SuppressWarnings("all")
  public List<SimpleGrantedAuthority> getRoles() {
    Claims claims = getClaims();
    List roles = (List) claims.get(roleClams);
    String json = JSONArray.toJSONString(roles);
    List<SimpleGrantedAuthority> grantedAuthorityList =
        JSONArray.parseArray(json, SimpleGrantedAuthority.class);
    return grantedAuthorityList;
  }

  public static void main(String[] args) {
    Map<String, Object> map = new HashMap<>();
    map.put("id", 123);
    JwtUtils build = JwtUtils.builder().prefixHeader().build();
    String generate = build.generate(new JwtArgsConfig(map));
    System.out.println("generate = " + generate);
    JwtUtils parse = JwtUtils.parseBuilder(generate).prefixHeader().build();
    Integer id = build.getClaim(generate, "id", Integer.class);
    Object id1 = parse.getClaim("id");
    System.out.println(id + ":::" + id1);
  }
}
